/**
 *    Copyright ${license.git.copyrightYears} the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.mybatis.generator.internal.db;

import org.mybatis.generator.api.FullyQualifiedTable;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.JavaTypeResolver;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.JavaReservedWords;
import org.mybatis.generator.config.*;
import org.mybatis.generator.internal.ObjectFactory;
import org.mybatis.generator.logging.Log;
import org.mybatis.generator.logging.LogFactory;

import java.sql.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.mybatis.generator.internal.util.JavaBeansUtil.getCamelCaseString;
import static org.mybatis.generator.internal.util.JavaBeansUtil.getValidPropertyName;
import static org.mybatis.generator.internal.util.StringUtility.*;
import static org.mybatis.generator.internal.util.messages.Messages.getString;

/**
 * The Class DatabaseIntrospector.
 *
 * @author Jeff Butler
 */
public class DatabaseIntrospector {

  /**
   * The database meta data.
   */
  private DatabaseMetaData databaseMetaData;

  /**
   * The java type resolver.
   */
  private JavaTypeResolver javaTypeResolver;

  /**
   * The warnings.
   */
  private List<String> warnings;

  /**
   * The context.
   */
  private Context context;

  /**
   * The logger.
   */
  private Log logger;

  /**
   * Instantiates a new database introspector.
   *
   * @param context
   *          the context
   * @param databaseMetaData
   *          the database meta data
   * @param javaTypeResolver
   *          the java type resolver
   * @param warnings
   *          the warnings
   */
  public DatabaseIntrospector(Context context, DatabaseMetaData databaseMetaData, JavaTypeResolver javaTypeResolver,
      List<String> warnings) {
    super();
    this.context = context;
    this.databaseMetaData = databaseMetaData;
    this.javaTypeResolver = javaTypeResolver;
    this.warnings = warnings;
    logger = LogFactory.getLog(getClass());
  }

  /**
   * Calculate primary key.
   *
   * @param table
   *          the table
   * @param introspectedTable
   *          the introspected table
   */
  private void calculatePrimaryKey(FullyQualifiedTable table, IntrospectedTable introspectedTable) {
    ResultSet rs = null;

    try {
      rs = databaseMetaData.getPrimaryKeys(table.getIntrospectedCatalog(), table.getIntrospectedSchema(),
          table.getIntrospectedTableName());
    } catch (SQLException e) {
      closeResultSet(rs);
      warnings.add(getString("Warning.15")); //$NON-NLS-1$
      return;
    }

    try {
      // keep primary columns in key sequence order
      Map<Short, String> keyColumns = new TreeMap<Short, String>();
      while (rs.next()) {
        String columnName = rs.getString("COLUMN_NAME"); //$NON-NLS-1$
        short keySeq = rs.getShort("KEY_SEQ"); //$NON-NLS-1$
        keyColumns.put(keySeq, columnName);
      }

      for (String columnName : keyColumns.values()) {
        introspectedTable.addPrimaryKeyColumn(columnName);
      }
    } catch (SQLException e) {
      // ignore the primary key if there's any error
    } finally {
      closeResultSet(rs);
    }
  }

  /**
   * Close result set.
   *
   * @param rs
   *          the rs
   */
  private void closeResultSet(ResultSet rs) {
    if (rs != null) {
      try {
        rs.close();
      } catch (SQLException e) {
        // ignore
      }
    }
  }

  /**
   * Report introspection warnings.
   *
   * @param introspectedTable
   *          the introspected table
   * @param tableConfiguration
   *          the table configuration
   * @param table
   *          the table
   */
  private void reportIntrospectionWarnings(IntrospectedTable introspectedTable, TableConfiguration tableConfiguration,
      FullyQualifiedTable table) {
    // make sure that every column listed in column overrides
    // actually exists in the table
    for (ColumnOverride columnOverride : tableConfiguration.getColumnOverrides()) {
      if (introspectedTable.getColumn(columnOverride.getColumnName()) == null) {
        warnings.add(getString("Warning.3", //$NON-NLS-1$
            columnOverride.getColumnName(), table.toString()));
      }
    }

    // make sure that every column listed in ignored columns
    // actually exists in the table
    for (String string : tableConfiguration.getIgnoredColumnsInError()) {
      warnings.add(getString("Warning.4", //$NON-NLS-1$
          string, table.toString()));
    }

    GeneratedKey generatedKey = tableConfiguration.getGeneratedKey();
    if (generatedKey != null && introspectedTable.getColumn(generatedKey.getColumn()) == null) {
      if (generatedKey.isIdentity()) {
        warnings.add(getString("Warning.5", //$NON-NLS-1$
            generatedKey.getColumn(), table.toString()));
      } else {
        warnings.add(getString("Warning.6", //$NON-NLS-1$
            generatedKey.getColumn(), table.toString()));
      }
    }

    for (IntrospectedColumn ic : introspectedTable.getAllColumns()) {
      if (JavaReservedWords.containsWord(ic.getJavaProperty())) {
        warnings.add(getString("Warning.26", //$NON-NLS-1$
            ic.getActualColumnName(), table.toString()));
      }
    }
  }

  /**
   * Returns a List of IntrospectedTable elements that matches the specified table configuration.
   *
   * @param tc
   *          the tc
   * @return a list of introspected tables
   * @throws SQLException
   *           the SQL exception
   */
  public List<IntrospectedTable> introspectTables(TableConfiguration tc) throws SQLException {

    // get the raw columns from the DB
    Map<ActualTableName, List<IntrospectedColumn>> columns = getColumns(tc);

    if (columns.isEmpty()) {
      warnings.add(getString("Warning.19", tc.getCatalog(), //$NON-NLS-1$
          tc.getSchema(), tc.getTableName()));
      return null;
    }

    removeIgnoredColumns(tc, columns);
    calculateExtraColumnInformation(tc, columns);
    applyColumnOverrides(tc, columns);
    calculateIdentityColumns(tc, columns);

    List<IntrospectedTable> introspectedTables = calculateIntrospectedTables(tc, columns);

    // now introspectedTables has all the columns from all the
    // tables in the configuration. Do some validation...

    Iterator<IntrospectedTable> iter = introspectedTables.iterator();
    while (iter.hasNext()) {
      IntrospectedTable introspectedTable = iter.next();

      if (!introspectedTable.hasAnyColumns()) {
        // add warning that the table has no columns, remove from the
        // list
        String warning = getString("Warning.1", introspectedTable.getFullyQualifiedTable().toString()); //$NON-NLS-1$
        warnings.add(warning);
        iter.remove();
      } else if (!introspectedTable.hasPrimaryKeyColumns() && !introspectedTable.hasBaseColumns()) {
        // add warning that the table has only BLOB columns, remove from
        // the list
        String warning = getString("Warning.18", introspectedTable.getFullyQualifiedTable().toString()); //$NON-NLS-1$
        warnings.add(warning);
        iter.remove();
      } else {
        // now make sure that all columns called out in the
        // configuration
        // actually exist
        reportIntrospectionWarnings(introspectedTable, tc, introspectedTable.getFullyQualifiedTable());
      }
    }

    return introspectedTables;
  }

  /**
   * Removes the ignored columns.
   *
   * @param tc
   *          the tc
   * @param columns
   *          the columns
   */
  private void removeIgnoredColumns(TableConfiguration tc, Map<ActualTableName, List<IntrospectedColumn>> columns) {
    for (Map.Entry<ActualTableName, List<IntrospectedColumn>> entry : columns.entrySet()) {
      Iterator<IntrospectedColumn> tableColumns = entry.getValue().iterator();
      while (tableColumns.hasNext()) {
        IntrospectedColumn introspectedColumn = tableColumns.next();
        if (tc.isColumnIgnored(introspectedColumn.getActualColumnName())) {
          tableColumns.remove();
          if (logger.isDebugEnabled()) {
            logger.debug(getString("Tracing.3", //$NON-NLS-1$
                introspectedColumn.getActualColumnName(), entry.getKey().toString()));
          }
        }
      }
    }
  }

  /**
   * Calculate extra column information.
   *
   * @param tc
   *          the tc
   * @param columns
   *          the columns
   */
  private void calculateExtraColumnInformation(TableConfiguration tc,
      Map<ActualTableName, List<IntrospectedColumn>> columns) {
    StringBuilder sb = new StringBuilder();
    Pattern pattern = null;
    String replaceString = null;
    if (tc.getColumnRenamingRule() != null) {
      pattern = Pattern.compile(tc.getColumnRenamingRule().getSearchString());
      replaceString = tc.getColumnRenamingRule().getReplaceString();
      replaceString = replaceString == null ? "" : replaceString; //$NON-NLS-1$
    }

    for (Map.Entry<ActualTableName, List<IntrospectedColumn>> entry : columns.entrySet()) {
      for (IntrospectedColumn introspectedColumn : entry.getValue()) {
        String calculatedColumnName;
        if (pattern == null) {
          calculatedColumnName = introspectedColumn.getActualColumnName();
        } else {
          Matcher matcher = pattern.matcher(introspectedColumn.getActualColumnName());
          calculatedColumnName = matcher.replaceAll(replaceString);
        }

        if (isTrue(tc.getProperty(PropertyRegistry.TABLE_USE_ACTUAL_COLUMN_NAMES))) {
          introspectedColumn.setJavaProperty(getValidPropertyName(calculatedColumnName));
        } else if (isTrue(tc.getProperty(PropertyRegistry.TABLE_USE_COMPOUND_PROPERTY_NAMES))) {
          sb.setLength(0);
          sb.append(calculatedColumnName);
          sb.append('_');
          sb.append(getCamelCaseString(introspectedColumn.getRemarks(), true));
          introspectedColumn.setJavaProperty(getValidPropertyName(sb.toString()));
        } else {
          introspectedColumn.setJavaProperty(getCamelCaseString(calculatedColumnName, false));
        }

        FullyQualifiedJavaType fullyQualifiedJavaType = javaTypeResolver.calculateJavaType(introspectedColumn);

        if (fullyQualifiedJavaType != null) {
          introspectedColumn.setFullyQualifiedJavaType(fullyQualifiedJavaType);
          introspectedColumn.setJdbcTypeName(javaTypeResolver.calculateJdbcTypeName(introspectedColumn));
        } else {
          // type cannot be resolved. Check for ignored or overridden
          boolean warn = true;
          if (tc.isColumnIgnored(introspectedColumn.getActualColumnName())) {
            warn = false;
          }

          ColumnOverride co = tc.getColumnOverride(introspectedColumn.getActualColumnName());
          if (co != null && stringHasValue(co.getJavaType()) && stringHasValue(co.getJavaType())) {
            warn = false;
          }

          // if the type is not supported, then we'll report a warning
          if (warn) {
            introspectedColumn.setFullyQualifiedJavaType(FullyQualifiedJavaType.getObjectInstance());
            introspectedColumn.setJdbcTypeName("OTHER"); //$NON-NLS-1$

            String warning = getString(
                "Warning.14", //$NON-NLS-1$
                Integer.toString(introspectedColumn.getJdbcType()), entry.getKey().toString(),
                introspectedColumn.getActualColumnName());

            warnings.add(warning);
          }
        }

        if (context.autoDelimitKeywords() && SqlReservedWords.containsWord(introspectedColumn.getActualColumnName())) {
          introspectedColumn.setColumnNameDelimited(true);
        }

        if (tc.isAllColumnDelimitingEnabled()) {
          introspectedColumn.setColumnNameDelimited(true);
        }
      }
    }
  }

  /**
   * Calculate identity columns.
   *
   * @param tc
   *          the tc
   * @param columns
   *          the columns
   */
  private void calculateIdentityColumns(TableConfiguration tc, Map<ActualTableName, List<IntrospectedColumn>> columns) {
    GeneratedKey gk = tc.getGeneratedKey();
    if (gk == null) {
      // no generated key, then no identity or sequence columns
      return;
    }

    for (Map.Entry<ActualTableName, List<IntrospectedColumn>> entry : columns.entrySet()) {
      for (IntrospectedColumn introspectedColumn : entry.getValue()) {
        if (isMatchedColumn(introspectedColumn, gk)) {
          if (gk.isIdentity() || gk.isJdbcStandard()) {
            introspectedColumn.setIdentity(true);
            introspectedColumn.setSequenceColumn(false);
          } else {
            introspectedColumn.setIdentity(false);
            introspectedColumn.setSequenceColumn(true);
          }
        }
      }
    }
  }

  /**
   * Checks if is matched column.
   *
   * @param introspectedColumn
   *          the introspected column
   * @param gk
   *          the gk
   * @return true, if is matched column
   */
  private boolean isMatchedColumn(IntrospectedColumn introspectedColumn, GeneratedKey gk) {
    if (introspectedColumn.isColumnNameDelimited()) {
      return introspectedColumn.getActualColumnName().equals(gk.getColumn());
    } else {
      return introspectedColumn.getActualColumnName().equalsIgnoreCase(gk.getColumn());
    }
  }

  /**
   * Apply column overrides.
   *
   * @param tc
   *          the tc
   * @param columns
   *          the columns
   */
  private void applyColumnOverrides(TableConfiguration tc, Map<ActualTableName, List<IntrospectedColumn>> columns) {
    for (Map.Entry<ActualTableName, List<IntrospectedColumn>> entry : columns.entrySet()) {
      for (IntrospectedColumn introspectedColumn : entry.getValue()) {
        ColumnOverride columnOverride = tc.getColumnOverride(introspectedColumn.getActualColumnName());

        if (columnOverride != null) {
          if (logger.isDebugEnabled()) {
            logger.debug(getString("Tracing.4", //$NON-NLS-1$
                introspectedColumn.getActualColumnName(), entry.getKey().toString()));
          }

          if (stringHasValue(columnOverride.getJavaProperty())) {
            introspectedColumn.setJavaProperty(columnOverride.getJavaProperty());
          }

          if (stringHasValue(columnOverride.getJavaType())) {
            introspectedColumn.setFullyQualifiedJavaType(new FullyQualifiedJavaType(columnOverride.getJavaType()));
          }

          if (stringHasValue(columnOverride.getJdbcType())) {
            introspectedColumn.setJdbcTypeName(columnOverride.getJdbcType());
          }

          if (stringHasValue(columnOverride.getTypeHandler())) {
            introspectedColumn.setTypeHandler(columnOverride.getTypeHandler());
          }

          if (columnOverride.isColumnNameDelimited()) {
            introspectedColumn.setColumnNameDelimited(true);
          }

          introspectedColumn.setGeneratedAlways(columnOverride.isGeneratedAlways());

          introspectedColumn.setProperties(columnOverride.getProperties());

        }
      }
    }
  }

  /**
   * This method returns a Map<ActualTableName, List<ColumnDefinitions>> of columns returned from the database
   * introspection.
   *
   * @param tc
   *          the tc
   * @return introspected columns
   * @throws SQLException
   *           the SQL exception
   */
  private Map<ActualTableName, List<IntrospectedColumn>> getColumns(TableConfiguration tc) throws SQLException {
    String localCatalog;
    String localSchema;
    String localTableName;

    boolean delimitIdentifiers = tc.isDelimitIdentifiers() || stringContainsSpace(tc.getCatalog())
        || stringContainsSpace(tc.getSchema()) || stringContainsSpace(tc.getTableName());

    if (delimitIdentifiers) {
      localCatalog = tc.getCatalog();
      localSchema = tc.getSchema();
      localTableName = tc.getTableName();
    } else if (databaseMetaData.storesLowerCaseIdentifiers()) {
      localCatalog = tc.getCatalog() == null ? null : tc.getCatalog().toLowerCase();
      localSchema = tc.getSchema() == null ? null : tc.getSchema().toLowerCase();
      localTableName = tc.getTableName() == null ? null : tc.getTableName().toLowerCase();
    } else if (databaseMetaData.storesUpperCaseIdentifiers()) {
      localCatalog = tc.getCatalog() == null ? null : tc.getCatalog().toUpperCase();
      localSchema = tc.getSchema() == null ? null : tc.getSchema().toUpperCase();
      localTableName = tc.getTableName() == null ? null : tc.getTableName().toUpperCase();
    } else {
      localCatalog = tc.getCatalog();
      localSchema = tc.getSchema();
      localTableName = tc.getTableName();
    }

    if (tc.isWildcardEscapingEnabled()) {
      String escapeString = databaseMetaData.getSearchStringEscape();

      StringBuilder sb = new StringBuilder();
      StringTokenizer st;
      if (localSchema != null) {
        st = new StringTokenizer(localSchema, "_%", true); //$NON-NLS-1$
        while (st.hasMoreTokens()) {
          String token = st.nextToken();
          if (token.equals("_") //$NON-NLS-1$
              || token.equals("%")) { //$NON-NLS-1$
            sb.append(escapeString);
          }
          sb.append(token);
        }
        localSchema = sb.toString();
      }

      sb.setLength(0);
      st = new StringTokenizer(localTableName, "_%", true); //$NON-NLS-1$
      while (st.hasMoreTokens()) {
        String token = st.nextToken();
        if (token.equals("_") //$NON-NLS-1$
            || token.equals("%")) { //$NON-NLS-1$
          sb.append(escapeString);
        }
        sb.append(token);
      }
      localTableName = sb.toString();
    }

    Map<ActualTableName, List<IntrospectedColumn>> answer = new HashMap<ActualTableName, List<IntrospectedColumn>>();

    if (logger.isDebugEnabled()) {
      String fullTableName = composeFullyQualifiedTableName(localCatalog, localSchema, localTableName, '.');
      logger.debug(getString("Tracing.1", fullTableName)); //$NON-NLS-1$
    }

    ResultSet rs = databaseMetaData.getColumns(localCatalog, localSchema, localTableName, "%"); //$NON-NLS-1$

    boolean supportsIsAutoIncrement = false;
    boolean supportsIsGeneratedColumn = false;
    ResultSetMetaData rsmd = rs.getMetaData();
    int colCount = rsmd.getColumnCount();
    for (int i = 1; i <= colCount; i++) {
      if ("IS_AUTOINCREMENT".equals(rsmd.getColumnName(i))) { //$NON-NLS-1$
        supportsIsAutoIncrement = true;
      }
      if ("IS_GENERATEDCOLUMN".equals(rsmd.getColumnName(i))) { //$NON-NLS-1$
        supportsIsGeneratedColumn = true;
      }
    }

    while (rs.next()) {
      IntrospectedColumn introspectedColumn = ObjectFactory.createIntrospectedColumn(context);

      introspectedColumn.setTableAlias(tc.getAlias());
      introspectedColumn.setJdbcType(rs.getInt("DATA_TYPE")); //$NON-NLS-1$
      introspectedColumn.setLength(rs.getInt("COLUMN_SIZE")); //$NON-NLS-1$
      introspectedColumn.setActualColumnName(rs.getString("COLUMN_NAME")); //$NON-NLS-1$
      introspectedColumn.setNullable(rs.getInt("NULLABLE") == DatabaseMetaData.columnNullable); //$NON-NLS-1$
      introspectedColumn.setScale(rs.getInt("DECIMAL_DIGITS")); //$NON-NLS-1$
      introspectedColumn.setRemarks(rs.getString("REMARKS")); //$NON-NLS-1$
      introspectedColumn.setDefaultValue(rs.getString("COLUMN_DEF")); //$NON-NLS-1$

      if (supportsIsAutoIncrement) {
        introspectedColumn.setAutoIncrement("YES".equals(rs.getString("IS_AUTOINCREMENT"))); //$NON-NLS-1$ //$NON-NLS-2$
      }

      if (supportsIsGeneratedColumn) {
        introspectedColumn.setGeneratedColumn("YES".equals(rs.getString("IS_GENERATEDCOLUMN"))); //$NON-NLS-1$ //$NON-NLS-2$
      }

      ActualTableName atn = new ActualTableName(rs.getString("TABLE_CAT"), //$NON-NLS-1$
          rs.getString("TABLE_SCHEM"), //$NON-NLS-1$
          rs.getString("TABLE_NAME")); //$NON-NLS-1$

      List<IntrospectedColumn> columns = answer.get(atn);
      if (columns == null) {
        columns = new ArrayList<IntrospectedColumn>();
        answer.put(atn, columns);
      }

      columns.add(introspectedColumn);

      if (logger.isDebugEnabled()) {
        logger.debug(getString(
            "Tracing.2", //$NON-NLS-1$
            introspectedColumn.getActualColumnName(), Integer.toString(introspectedColumn.getJdbcType()),
            atn.toString()));
      }
    }

    closeResultSet(rs);

    if (answer.size() > 1 && !stringContainsSQLWildcard(localSchema) && !stringContainsSQLWildcard(localTableName)) {
      // issue a warning if there is more than one table and
      // no wildcards were used
      ActualTableName inputAtn = new ActualTableName(tc.getCatalog(), tc.getSchema(), tc.getTableName());

      StringBuilder sb = new StringBuilder();
      boolean comma = false;
      for (ActualTableName atn : answer.keySet()) {
        if (comma) {
          sb.append(',');
        } else {
          comma = true;
        }
        sb.append(atn.toString());
      }

      warnings.add(getString("Warning.25", //$NON-NLS-1$
          inputAtn.toString(), sb.toString()));
    }

    return answer;
  }

  /**
   * Calculate introspected tables.
   *
   * @param tc
   *          the tc
   * @param columns
   *          the columns
   * @return the list
   */
  private List<IntrospectedTable> calculateIntrospectedTables(TableConfiguration tc,
      Map<ActualTableName, List<IntrospectedColumn>> columns) {
    boolean delimitIdentifiers = tc.isDelimitIdentifiers() || stringContainsSpace(tc.getCatalog())
        || stringContainsSpace(tc.getSchema()) || stringContainsSpace(tc.getTableName());

    List<IntrospectedTable> answer = new ArrayList<IntrospectedTable>();

    for (Map.Entry<ActualTableName, List<IntrospectedColumn>> entry : columns.entrySet()) {
      ActualTableName atn = entry.getKey();

      // we only use the returned catalog and schema if something was
      // actually
      // specified on the table configuration. If something was returned
      // from the DB for these fields, but nothing was specified on the
      // table
      // configuration, then some sort of DB default is being returned
      // and we don't want that in our SQL
      FullyQualifiedTable table = new FullyQualifiedTable(stringHasValue(tc.getCatalog()) ? atn.getCatalog() : null,
          stringHasValue(tc.getSchema()) ? atn.getSchema() : null, atn.getTableName(), tc.getDomainObjectName(),
          tc.getAlias(), isTrue(tc.getProperty(PropertyRegistry.TABLE_IGNORE_QUALIFIERS_AT_RUNTIME)),
          tc.getProperty(PropertyRegistry.TABLE_RUNTIME_CATALOG),
          tc.getProperty(PropertyRegistry.TABLE_RUNTIME_SCHEMA),
          tc.getProperty(PropertyRegistry.TABLE_RUNTIME_TABLE_NAME), delimitIdentifiers, context);

      IntrospectedTable introspectedTable = ObjectFactory.createIntrospectedTable(tc, table, context);

      for (IntrospectedColumn introspectedColumn : entry.getValue()) {
        introspectedTable.addColumn(introspectedColumn);
      }

      calculatePrimaryKey(table, introspectedTable);

      enhanceIntrospectedTable(introspectedTable);

      answer.add(introspectedTable);
    }

    return answer;
  }

  /**
   * This method calls database metadata to retrieve some extra information about the table such as remarks associated
   * with the table and the type.
   * <p>
   * If there is any error, we just add a warning and continue.
   *
   * @param introspectedTable
   */
  private void enhanceIntrospectedTable(IntrospectedTable introspectedTable) {
    try {
      FullyQualifiedTable fqt = introspectedTable.getFullyQualifiedTable();

      ResultSet rs = databaseMetaData.getTables(fqt.getIntrospectedCatalog(), fqt.getIntrospectedSchema(),
          fqt.getIntrospectedTableName(), null);
      if (rs.next()) {
        String remarks = rs.getString("REMARKS"); //$NON-NLS-1$
        String tableType = rs.getString("TABLE_TYPE"); //$NON-NLS-1$

        introspectedTable.setTableType(tableType);
        introspectedTable.setRemarks(remarks);
      }
      closeResultSet(rs);

      // 更改获取表注释信息代码(原来的方式获取表注释为空,故更改) update by Hudan 2017-02-05
      if (introspectedTable.getRemarks() == null || "".equals(introspectedTable.getRemarks().trim())) {
        String remarks = null;
        Statement stmt = databaseMetaData.getConnection().createStatement();
        rs = stmt.executeQuery("SHOW TABLE STATUS LIKE '" + fqt.getIntrospectedTableName() + "'");
        while (rs.next()) {
          remarks = rs.getString("COMMENT");
        }
        closeResultSet(rs);
        stmt.close();
        introspectedTable.setRemarks(remarks);
      }
    } catch (SQLException e) {
      warnings.add(getString("Warning.27", e.getMessage())); //$NON-NLS-1$
    }
  }
}
